<?php
	
	namespace org\tekuna\framework\action;

	use org\tekuna\core\context\Context;
	use org\tekuna\core\event\Event;
	use org\tekuna\core\event\AbstractEventListener;
	use org\tekuna\core\configuration\ConfigurationElement;
	use org\tekuna\core\configuration\ConfigurationException;
	
	use org\tekuna\framework\request\Request;
	use org\tekuna\framework\info\InformationEvent;
	
	
	/**
	 * This event is triggered when a certain Action was mapped and
	 * may be processed by the InvocationChain. Usually a CliActionEvent
	 * or HttpActionEvent will be triggered.
	 */
	abstract class ActionEvent extends AbstractEventListener implements Event {
		
		protected
			$objComponentElement,
			$objAction;


		/**
		 * Constuct a new ActionEvent.
		 * 
		 * @param Context $objContext the application context
		 * @param ConfigurationElement $objComponentElement the matched component element
		 * @param ConfigurationElement $objActionElement the matched action element
		 */
		public function __construct(Context $objContext, ConfigurationElement $objComponentElement, ConfigurationElement $objActionElement) {
			
			$this -> objComponentElement = $objComponentElement;
			$this -> objAction = self :: getActionInstance($objContext, $objActionElement);
			$objContext -> getEventManager() -> registerListener($this);
		}
		
		/**
		 * @return ConfigurationElement returns the configuration element of
		 *         the action's component
		 */
		public function getComponentElement() {
			
			return $this -> objComponentElement;
		}
		
		/**
		 * @return ConfigurationElement returns the configuration element of
		 *         the action
		 */
		public function getActionElement() {
			
			return $this -> objAction -> getConfigurationElement();
		}
		
		/**
		 * @return Action returns the action instance of this Event.
		 */
		public function getAction() {
			
			return $this -> objAction;
		}
		
		/**
		 * Builds an Action object from a given ConfigurationElement in the given
		 * application context
		 * 
		 * @param Context $objContext the application context that is injected into the action
		 * @param ConfigurationElement $objActionElement the configuration element that defines the action's class
		 * @throws ConfigurationException if the action class does not exist
		 * @throws ActionException if the action class does not implement the Action interface
		 */
		public static function getActionInstance(Context $objContext, ConfigurationElement $objActionElement) {

			// check that the class parameter exists
			if (! $objActionElement -> hasAttribute('framework', 'class')) {

				throw new ConfigurationException("The mapped action element does not have the 'class' parameter in matched action ". trim($objActionElement -> __toString()));
			}

			// get the class name from the class parameter
			$sActionClass = $objActionElement -> getAttribute('framework', 'class') -> getValue();

			// validate the class parameter
			if (trim($sActionClass) == '') {

				throw new ConfigurationException("The string '$sActionClass' is no valid action class identifier in matched action ". trim($objActionElement -> __toString()));
			}

			// throw exception if action class does not exist (triggers class loading)
			if (! class_exists($sActionClass)) {
				
				throw new ConfigurationException("The action class '$sActionClass' does not exist in configured action ". trim($objActionElement -> __toString()));
			}
			
			// instantiate the action
			$objAction = new $sActionClass();

			// check the interface implementation
			if (! $objAction instanceof Action) {

				throw new ActionException("The class '$sActionClass' does not implement the org\\tekuna\\framework\\action\\Action interface in matched action ". trim($objActionElement -> __toString()));
			}

			// inject dependencies
			$objAction -> setApplicationContext($objContext);
			$objAction -> setConfigurationElement($objActionElement);
			
			return $objAction;
		}
		
		/**
		 * @return boolean returns true for an InformationEvent
		 */
		public function handlesEvent(Event $objEvent) {
			
			return $objEvent instanceof InformationEvent;
		}
	}
